#include <thread>
#include <memory>
#include <ctime>
#include <vector>
#include <string>
#include <cassert>
#include <sstream>
#include <unordered_map>

#include "util.hpp"
#include "level.hpp"
#include "message.hpp"

namespace log_sys{
    class FormatItem{
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream& out, const LogMsg& msg) = 0;
    };

    class MsgFormatItem : public FormatItem {
    public:
        MsgFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << msg._payload;
        }
    };

    class LevelFormatItem : public FormatItem {
    public:
        LevelFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << log_sys::Log_level::logleveltostring(msg._level);
        }
    };
    
    class LoggerFormatItem : public FormatItem {
    public:
        LoggerFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << msg._logger;
        }
    };

    class ThreadFormatItem : public FormatItem {
    public:
        ThreadFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << msg._tid;
        }
    };
    
    class TimeFormatItem : public FormatItem {
    private:
        std::string _format;
    public:
        TimeFormatItem(const std::string &format = "%H:%M:%S")
            :_format(format)
        {
            if (format.empty()) _format = "%H:%M:%S";
        }

        virtual void format(std::ostream &os, const LogMsg &msg) {
            // 专属于时间类的结构体
            struct tm t;
            // localtime_r 将 msg 中的 ctime 成员放到 t 中
            localtime_r(&msg._ctime, &t);
            char tmp[32] = {0};
            // 将 t 中存放的内容按照当前类在构造时使用的 _format 中存放的格式放到tmp中
            strftime(tmp, 31, _format.c_str(), &t);
            os << tmp;
        }
    };
    
    class FileFormatItem : public FormatItem {
    public:
        FileFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << msg._file;
        }
    };
    
    class LineFormatItem : public FormatItem {
    public:
        LineFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << msg._line;
        }
    };

    class TabFormatItem : public FormatItem {
    public:
        TabFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << "\t";
        }
    };
    
    class NLineFormatItem : public FormatItem {
    public:
        NLineFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << "\n";
        }
    };
    
    class OtherFormatItem : public FormatItem {
    private:
        std::string _str;
    public:
        OtherFormatItem(const std::string &str = ""):_str(str){}
        virtual void format(std::ostream &os, const LogMsg &msg) {
            os << _str;
        }
    };

    class Formatter{
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter (const std::string & pattern = "[%d{%H:%M:%S}[%t][%c][%f:%l][%p]%T%m%n]")
            :_pattern(pattern)
        {
            assert(parsePattern());
        }

        void format(std::ostream& out, const LogMsg& msg){
            // 将 _items 的指针一个个通过自己指向的对象的类型经多态调用 把msg这种的内容输出到对应的out中
            for(auto& item : _items)
            {
                item->format(out, msg);
            }
        }

        // 
        std::string format(const LogMsg& msg){
            std::stringstream ss;
            // 调用上面的那个format, 由于format 需要自己传一个 std::stringstream 类型的参数非常怪，
            // 所以使用了这种设计
            format(ss, msg);
            return ss.str();
        }

        // abcd[%d{%H:%M:%S}][%p]%T%m%n
        // key=nullptr, val=abcde[ 
        // key=d, val = %H:%M:%S 
        // key=nullptr, val = ][
        // key=p, val=nullptr
        // key=nullptr, val=]
        // key=T, val=nullptr
        // key=m, val=nullptr
        // key=n, val=nullptr
    private:
        bool parsePattern(){
            // 1. 对格式化规则字符串进行解析
            //    ab%%cde[%d{%H:%M:%S}][%p]%T%m%n
            std::vector<std::pair<std::string, std::string>> fmt_order;
            size_t pos = 0;
            // val里面存放的都是原始字符，注意这里的原始字符包括{%H:%M:%S} 也是原始字符（包括百分号）
            std::string key, val;
            while (pos < _pattern.size())
            {
                // 判断当前遍历到的字符是不是%，
                // 如果不是%，则将当前字符视作原始字符
                //     注意：这里可以说明当前的字符前一个字符不是百分号(后面的逻辑将会保证这一点)
                // 然后跳过后面的逻辑
                if (_pattern[pos] != '%')
                {
                    val.push_back(_pattern[pos++]);
                    continue;
                }
                
                // 由于 continue 的存在，如果程序走到这里，遍历到一个 %
                // 此时 先判断有没有两个 % 连续出现的情况，有两个情形
                // case1: 出现了，则将第二个百分号视作原始字符
                //        然后跳过后面的逻辑
                if(pos + 1 < _pattern.size() && _pattern[pos + 1] == '%')
                {
                    val.push_back('%');
                    pos += 2;
                    continue;
                }

                // case2: 没出现，百分号之后的那个字符作为原始字符，添加到fmt_order中
                //        此时key不需要任何格式字符串来表现
                if (!val.empty())
                {
                    fmt_order.push_back(std::make_pair("", val));
                    val.clear();
                }                

                // 如果程序走到这里，说明当前字符是 %，下面两种情况肯定发生了其中一种
                // 情况一：pos + 1 == _pattern.size(), 说明_pattern以 % 结尾了
                if (pos + 1 == _pattern.size())
                {
                    // 如果程序走到这里，说明_pattern以 % 结尾了
                    std::cout << "%之后，没有对应的格式化字符串了！给的内容字符串有问题\n";
                    return false;
                }
                
                // 情况二：_pattern[pos + 1] != '%'
                // 发现 _pattern[pos + 1] 不是 % 之后，需要进行两步操作
                // 第一步，将 _pattern[pos + 1] 放到 key中，
                // 第二步：判断 _pattern[pos + 2] 是否是 '{'，有以下两种情况
                // 情况一：_pattern[pos + 2] 是 '{'
                //     此时需要继续往后遍历，一直找到一个 '}'
                //     在这期间遍历到的所有字符都是都尾插到 val 后
                key = _pattern[pos + 1];
                pos += 2;
                if (pos < _pattern.size() && _pattern[pos] == '{')
                {
                    pos += 1; // 让pos指向'{' 后面的那个位置
                    while (pos < _pattern.size() && _pattern[pos] != '}')
                    {
                        val.push_back(_pattern[pos++]);
                    }
                    if (pos == _pattern.size())
                    {
                        // 走到这说明找到了一个 '{', 但是没有找到 '}', 说明出错了
                        std::cout << "子项匹配{}中出错！" << std::endl;
                        return false;
                    }
                    // 走到这说明pos的位置让 _pattern[pos] == '}'的位置
                    // 下面的操作都是在输出 key 和 val,
                    // 此时下一次的训话需要从 '}' 的下一个位置开始
                    pos += 1;
                }
                // 情况二：_pattern[pos + 2] 不是 '{'，
                fmt_order.push_back(std::make_pair(key, val));
                key.clear();
                val.clear();
            }

            for(auto &it : fmt_order){
                _items.push_back(createItem(it.first, it.second));
            }
            return true; 
        }
    private:
        // 根据不同的格式化字符创建不同的格式化子项对象
        FormatItem::ptr createItem(const std::string & key, const std::string& val){
            /*
                %d 表示日期，包含子格式 {%H:%M:%S}
                %t 表示线程ID
                %c 表示日志器名称
                %f 表示源码文件名
                %l 表示源码行号
                %p 表示日志级别 
                %T 表示制表符缩进
                %m 表示主题消息
                %n 表示换行
            */
            if(key == "d") return std::make_shared<TimeFormatItem>(val);
            if(key == "t") return std::make_shared<ThreadFormatItem>();
            if(key == "c") return std::make_shared<LoggerFormatItem>();
            if(key == "f") return std::make_shared<FileFormatItem>();
            if(key == "l") return std::make_shared<LineFormatItem>();
            if(key == "p") return std::make_shared<LevelFormatItem>();
            if(key == "T") return std::make_shared<TabFormatItem>();
            if(key == "m") return std::make_shared<MsgFormatItem>();
            if(key == "n") return std::make_shared<NLineFormatItem>();
            return std::make_shared<OtherFormatItem>(val);
        }
        std::string _pattern; // 格式化规则字符串
        std::vector<FormatItem::ptr> _items;
    };
}
